home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
edit
/
thesrc20.zip
/
sort.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-26
|
22KB
|
524 lines
/***********************************************************************/
/* SORT.C - Functions related to the SORT command. */
/***********************************************************************/
/*
* THE - The Hessling Editor. A text editor similar to VM/CMS xedit.
* Copyright (C) 1991-1995 Mark Hessling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
*
* The Free Software Foundation, Inc.
* 675 Mass Ave,
* Cambridge, MA 02139 USA.
*
*
* If you make modifications to this software that you feel increases
* it usefulness for the rest of the community, please email the
* changes, enhancements, bug fixes as well as any and all ideas to me.
* This software is going to be maintained and enhanced as deemed
* necessary by the community.
*
* Mark Hessling email: M.Hessling@gu.edu.au
* 36 David Road Phone: +61 7 849 7731
* Holland Park Fax: +61 7 875 5314
* QLD 4121
* Australia
*/
/*
$Id: SORT.C 2.0 1995/01/26 16:34:40 MH Release MH $
*/
#include <stdio.h>
#include "the.h"
#include "proto.h"
#define MAX_SORT_FIELDS 10
#define SF_ERROR 0
#define SF_START 1
#define SF_ORDER 2
#define SF_LEFT 3
struct sort_field
{
CHARTYPE order; /* A - ascending, D - descending */
LENGTHTYPE left_col; /* left column */
LENGTHTYPE right_col; /* right column */
};
typedef struct sort_field SORT_FIELD;
CHARTYPE *sort_field_1;
CHARTYPE *sort_field_2;
SORT_FIELD sort_fields[MAX_SORT_FIELDS];
short num_fields;
#ifdef __STDC__
static int cmp(const void *,const void *);
#else
static int cmp();
#endif
/***********************************************************************/
#ifdef __STDC__
static int cmp(const void *first,const void *second)
#else
static int cmp(first,second)
void *first,*second;
#endif
/***********************************************************************/
{
/*-------------------------- external data ----------------------------*/
/*--------------------------- local data ------------------------------*/
register short i=0,j=0;
short len=0,rc=RC_OK;
LENGTHTYPE right_col=0,left_col=0;
LINE *one = *(LINE **)first;
LINE *two = *(LINE **)second;
/*--------------------------- processing ------------------------------*/
/*---------------------------------------------------------------------*/
/* For each sort field defined in the array sort_fields, compare the */
/* value of both lines for the specified column range. */
/*---------------------------------------------------------------------*/
for (i=0;i<num_fields;i++)
{
/*---------------------------------------------------------------------*/
/* Calculate the length of the sort field. */
/*---------------------------------------------------------------------*/
len = sort_fields[i].right_col - sort_fields[i].left_col + 1;
/*---------------------------------------------------------------------*/
/* Set the two temporary fields to blanks. */
/*---------------------------------------------------------------------*/
memset(sort_field_1,' ',len);
memset(sort_field_2,' ',len);
/*---------------------------------------------------------------------*/
/* For the first line to be compared, extract the portion of the line */
/* that corresponds with the current sort column... */
/*---------------------------------------------------------------------*/
right_col = min(sort_fields[i].right_col,one->length);
left_col = min(sort_fields[i].left_col,one->length);
/*---------------------------------------------------------------------*/
/* If the sort column lies after the end of the line, leave the */
/* contents of the sort field blank. */
/*---------------------------------------------------------------------*/
if (sort_fields[i].left_col <= one->length)
memcpy(sort_field_1,one->line+left_col-1,right_col-left_col+1);
/*---------------------------------------------------------------------*/
/* For the next line to be compared, extract the portion of the line */
/* that corresponds with the current sort column... */
/*---------------------------------------------------------------------*/
right_col = min(sort_fields[i].right_col,two->length);
left_col = min(sort_fields[i].left_col,two->length);
/*---------------------------------------------------------------------*/
/* If the sort column lies after the end of the line, leave the */
/* contents of the sort field blank. */
/*---------------------------------------------------------------------*/
if (sort_fields[i].left_col <= two->length)
memcpy(sort_field_2,two->line+left_col-1,right_col-left_col+1);
/*---------------------------------------------------------------------*/
/* If CASE IGNORE is on for the current view, set both sort fields to */
/* uppercase for the comparison. */
/*---------------------------------------------------------------------*/
if (CURRENT_VIEW->case_sort == CASE_IGNORE)
{
for (j=0;j<len;j++)
{
if (islower(sort_field_1[j]))
sort_field_1[j] = toupper(sort_field_1[j]);
if (islower(sort_field_2[j]))
sort_field_2[j] = toupper(sort_field_2[j]);
}
}
/*---------------------------------------------------------------------*/
/* If the two sort fields are equal, continue the sort with the next */
/* sort field value. If the sort fields are different, return with the */
/* the comparison value (if ASCENDING) or the comparison value negated */
/* (if DESCENDING). */
/*---------------------------------------------------------------------*/
if ((rc = strncmp(sort_field_1,sort_field_2,len)) != 0)
return((sort_fields[i].order == 'A') ? rc : -rc);
}
/*---------------------------------------------------------------------*/
/* To get to here, the result of sorting on all fields has resulted in */
/* both lines being equal. Return with 0 to indicate this. */
/*---------------------------------------------------------------------*/
return(0);
}
/***********************************************************************/
#ifdef PROTO
short execute_sort(CHARTYPE *params)
#else
short execute_sort(params)
#endif
/***********************************************************************/
{
/*-------------------------- external data ----------------------------*/
extern bool in_profile;
extern CHARTYPE *temp_cmd;
extern VIEW_DETAILS *vd_mark;
/*--------------------------- local data ------------------------------*/
#define SOR_PARAMS 32
register int i=0;
CHARTYPE *word[SOR_PARAMS+1];
unsigned short num_params=0;
LINE **lfirst=NULL,**lp=NULL,*curr=NULL,*first=NULL,*last=NULL;
LINETYPE num_lines=0L,j=0L,true_line=0L,dest_line=0L;
short rc=RC_OK,direction=DIRECTION_FORWARD;
LENGTHTYPE left_col=0,right_col=0,max_column_width=0;
short state=0;
CHARTYPE order='A';
TARGET target;
short target_type=TARGET_NORMAL|TARGET_BLOCK_CURRENT|TARGET_ALL|TARGET_SPARE;
bool save_scope_all=FALSE;
/*--------------------------- processing ------------------------------*/
#ifdef TRACE
trace_function("sort.c: execute_sort");
#endif
/*---------------------------------------------------------------------*/
/* Save the current setting of scope_all to fool the validate_target() */
/* routine into thinking scope_all is in effect. */
/*---------------------------------------------------------------------*/
save_scope_all = CURRENT_VIEW->scope_all;
/*---------------------------------------------------------------------*/
/* Validate first argument as a target... */
/*---------------------------------------------------------------------*/
initialise_target(&target);
rc = validate_target(params,&target,target_type,get_true_line(),TRUE,TRUE);
/*---------------------------------------------------------------------*/
/* Restore the setting of scope_all before anything else is done. */
/*---------------------------------------------------------------------*/
CURRENT_VIEW->scope_all = save_scope_all;
if (rc != RC_OK)
{
free_target(&target);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
}
true_line = target.true_line;
num_lines = target.num_lines;
dest_line = target.true_line + target.num_lines -1L;
direction = DIRECTION_FORWARD;
if (num_lines > 0L
&& true_line == 0L)
{
true_line++;
num_lines--;
}
if (num_lines < 0L
&& true_line == CURRENT_FILE->number_lines+1L)
{
true_line--;
num_lines++;
}
if (num_lines < 0L)
{
dest_line = target.true_line + target.num_lines +1L;
true_line = true_line + num_lines + 1L;
num_lines = num_lines * (-1L);
direction = DIRECTION_BACKWARD;
}
/*---------------------------------------------------------------------*/
/* Don't need to do anything if < 2 lines to be sorted. */
/*---------------------------------------------------------------------*/
if (num_lines < 2L)
{
free_target(&target);
display_error(55,(CHARTYPE *)"",FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_OK);
}
/*---------------------------------------------------------------------*/
/* Parse the remainder of the arguments and set up the sort_fields[] */
/* array with valid values. */
/*---------------------------------------------------------------------*/
if (target.spare != (-1))
num_params = param_split(strtrunc(target.rt[target.spare].string),word,SOR_PARAMS,WORD_DELIMS,TEMP_PARAM);
/*---------------------------------------------------------------------*/
/* Process parameters differently, depending on the number... */
/* 0 parameter (target only) - if BOX BLOCK, then the sort field will */
/* be the block columns, otherwise ZONE */
/* settings will be used. */
/* 1 parameters (target & order) - same as above but also validate the */
/* ordering value. */
/* > 1 parameters (target & sort fields) - validate each parameter. */
/*---------------------------------------------------------------------*/
switch(num_params)
{
case 0:
case 1:
sort_fields[0].left_col = CURRENT_VIEW->zone_start;
sort_fields[0].right_col = CURRENT_VIEW->zone_end;
sort_fields[0].order = order;
num_fields = 1;
if (target.rt[0].target_type == TARGET_BLOCK_CURRENT
&& MARK_VIEW->mark_type == M_BOX)
{
sort_fields[0].left_col = MARK_VIEW->mark_start_col;
sort_fields[0].right_col = MARK_VIEW->mark_end_col;
}
/*---------------------------------------------------------------------*/
/* No more processing if only 1 parameter. */
/*---------------------------------------------------------------------*/
if (num_params == 0)
break;
/*---------------------------------------------------------------------*/
/* Processing for 2 parameters; validate ordering value. */
/*---------------------------------------------------------------------*/
if (equal((CHARTYPE *)"ascending",word[0],1)
|| equal((CHARTYPE *)"descending",word[0],1))
{
order = word[0][0];
if (islower(order))
order = toupper(order);
sort_fields[0].order = order;
break;
}
/*---------------------------------------------------------------------*/
/* If the parameter is not Ascending or Descending, display error. */
/*---------------------------------------------------------------------*/
display_error(1,(CHARTYPE *)word[0],FALSE);
free_target(&target);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
break;
/*---------------------------------------------------------------------*/
/* More than 1 parameters; sort field(s) are being specified. */
/*---------------------------------------------------------------------*/
default:
i = 0;
num_fields = 0;
state = SF_START;
while(1)
{
switch(state)
{
case SF_START:
if (equal((CHARTYPE *)"ascending",word[i],1)
|| equal((CHARTYPE *)"descending",word[i],1))
{
order = word[i][0];
if (islower(order))
order = toupper(order);
sort_fields[num_fields].order = order;
state = SF_ORDER;
i++;
break;
}
left_col = atoi(word[i]);
if (left_col == 0)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].order = order;
sort_fields[num_fields].left_col = left_col;
state = SF_LEFT;
i++;
break;
case SF_ORDER:
left_col = atoi(word[i]);
if (left_col < 1)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].left_col = left_col;
state = SF_LEFT;
i++;
break;
case SF_LEFT:
right_col = atoi(word[i]);
if (right_col < 1)
{
state = SF_ERROR;
break;
}
sort_fields[num_fields].right_col = right_col;
if (right_col < left_col)
{
state = SF_ERROR;
break;
}
state = SF_START;
i++;
num_fields++;
break;
default:
state = SF_ERROR;
break;
}
/*---------------------------------------------------------------------*/
/* If we have an error, display a message... */
/*---------------------------------------------------------------------*/
if (state == SF_ERROR)
{
free_target(&target);
display_error(1,(CHARTYPE *)word[i],FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
}
/*---------------------------------------------------------------------*/
/* If we have run out of parameters... */
/*---------------------------------------------------------------------*/
if (i == num_params)
{
/*---------------------------------------------------------------------*/
/* ...then if we have the correct number of parameters, OK. */
/*---------------------------------------------------------------------*/
if (state == SF_START)
break;
else
/*---------------------------------------------------------------------*/
/* ...otherwise display an error. */
/*---------------------------------------------------------------------*/
{
free_target(&target);
display_error(1,strtrunc(target.rt[target.spare].string),FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_INVALID_OPERAND);
}
}
}
break;
}
/*---------------------------------------------------------------------*/
/* Determine the maximum length of a sort field. */
/*---------------------------------------------------------------------*/
for (i=0;i<num_fields;i++)
max_column_width = max(max_column_width,
sort_fields[i].right_col - sort_fields[i].left_col + 1);
/*---------------------------------------------------------------------*/
/* Allocate memory for each of the temporary sort fields to the length */
/* of the maximum field width. */
/*---------------------------------------------------------------------*/
if ((sort_field_1 = (CHARTYPE *)(*the_malloc)(max_column_width)) == NULL)
{
free_target(&target);
display_error(30,(CHARTYPE *)"",FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
if ((sort_field_2 = (CHARTYPE *)(*the_malloc)(max_column_width)) == NULL)
{
free_target(&target);
display_error(30,(CHARTYPE *)"",FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
/*---------------------------------------------------------------------*/
/* Assign the values of the newly allocated array to the LINE pointers */
/* for the target lines. */
/*---------------------------------------------------------------------*/
first = curr = lll_find(CURRENT_FILE->first_line,true_line);
#ifdef MSWIN
wqsort(curr,(long)num_lines,(int)sizeof(LINE *),cmp);
#else
/*---------------------------------------------------------------------*/
/* Allocate memory for num_lines of LINE pointers. */
/*---------------------------------------------------------------------*/
if ((lfirst = (LINE **)(*the_malloc)(num_lines*sizeof(LINE *))) == NULL)
{
free_target(&target);
display_error(30,(CHARTYPE *)"",FALSE);
#ifdef TRACE
trace_return();
#endif
return(RC_OUT_OF_MEMORY);
}
lp = lfirst;
for (j=0L;j<num_lines;j++)
{
lp[j] = curr;
curr = curr->next;
}
last = curr;
/*---------------------------------------------------------------------*/
/* Sort the target array... */
/*---------------------------------------------------------------------*/
qsort(lfirst,num_lines,sizeof(LINE *),cmp);
/*---------------------------------------------------------------------*/
/* Merge the sorted array pointers into the linked list... */
/*---------------------------------------------------------------------*/
lp = lfirst;
first->prev->next = lp[0];
for (j=0L;j<num_lines;j++)
{
if (j == 0L)
{
lp[0]->prev = first->prev;
lp[0]->next = lp[1];
}
else
if (j == num_lines - 1L)
{
lp[num_lines-1]->next = last;
lp[num_lines-1]->prev = lp[num_lines-2L];
last->prev = lp[num_lines-1L];
}
else
{
lp[j]->next = lp[j+1L];
lp[j]->prev = lp[j-1L];
}
}
#endif
/*---------------------------------------------------------------------*/
/* If STAY is OFF, change the current and focus lines by the number */
/* of lines calculated from the target. */
/*---------------------------------------------------------------------*/
if (!CURRENT_VIEW->stay /* stay is off */
&& target.num_lines != 0L)
CURRENT_VIEW->focus_line = CURRENT_VIEW->current_line = find_next_in_scope(NULL,dest_line,direction);
/*---------------------------------------------------------------------*/
/* Free up the memory used for the sort fields and the target array. */
/*---------------------------------------------------------------------*/
(*the_free)(sort_field_1);
(*the_free)(sort_field_2);
(*the_free)(lfirst);
free_target(&target);
/*---------------------------------------------------------------------*/
/* Increment alteration count... */
/*---------------------------------------------------------------------*/
if ((rc = increment_alt(CURRENT_FILE)) != RC_OK)
{
#ifdef TRACE
trace_return();
#endif
return(rc);
}
sprintf(temp_cmd,"%ld line(s) sorted",num_lines);
display_error(0,temp_cmd,TRUE);
#ifdef TRACE
trace_return();
#endif
return(RC_OK);
}